package org.activequant.tradesystem.ib;
import org.activequant.container.ITradeSystemContextThread;
import org.activequant.core.domainmodel.data.Candle;
import org.activequant.core.domainmodel.data.MarketDataEntity;
import org.activequant.core.domainmodel.data.Quote;
import org.activequant.core.domainmodel.data.TickerNews;
import org.activequant.core.domainmodel.data.TradeIndication;
import org.activequant.core.domainmodel.marketdepth.MarketDepthEvent;
import org.activequant.data.retrieval.IMarketDataReplayService;
import org.apache.log4j.Logger;

import com.ib.algokit.RunMe;

/**
 * The BacktestRunner fetches from the replay service all 
 * data entities and triggers the replay service's fire method. <br/>
 * An ITradeSystemContextThread implementation. Holds the following associated variable:
 * <ul>
 * <li>replayService(IMarketDataReplayService)</li>
 * </ul>
 * <br>
 * <b>History:</b><br/>
 *  - [17.01.2008] Created (Erik Nijkamp)<br/>
 *  - [16.05.2009] Added Throughtput tracking (Ulrich Staudinger)<br/>
 *
 *  @author Erik Nijkamp
 *  @author Ulrich Staudinger
 */
public class BacktestRunner implements ITradeSystemContextThread {
	
	private static final Logger log = Logger.getLogger(BacktestRunner.class);
	/**
	 * private IMarketDataReplayService replayService;
	 */
	private IMarketDataReplayService replayService;
	
	public static int NUMBER_OF_FOREX_PAIRS=0;
	public static int NUMBER_OF_INSTRUMENTS=0;
	
	/**
	 * an empty BacktestRunner(ITradeSystemContextThread implementation) constructor
	 */
	public BacktestRunner() { }
	/**
	 * constructs a BacktestRunner(ITradeSystemContextThread implementation) using the given replayService(IMarketDataReplayService) 
	 * to set its associated replayService(IMarketDataReplayService)
	 * @param replayService
	 */
	public BacktestRunner(IMarketDataReplayService replayService) {
		this.replayService = replayService;
	}
	
	public void run() {
		log.info("running backtester");
		int STREAMS_PER_INSTRUMENT=4;
		int STREAMS_PER_FOREX_PAIR=1;
		if(RunMe.isMarketDepthEnabled){
			STREAMS_PER_INSTRUMENT++;
		}
		while (/*NUMBER_OF_INSTRUMENTS==0 ||*/ replayService.getStreamsSize()< ((NUMBER_OF_INSTRUMENTS*STREAMS_PER_INSTRUMENT)+(NUMBER_OF_FOREX_PAIRS*STREAMS_PER_FOREX_PAIR)) ){
			try {
				log.info("sleeping 10 sec");
				Thread.sleep(10000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		int count = 0;
		int quoteCount = 0;
		int candleCount = 0;
		int tradeIndicationCount = 0;
		int marketDepthEventCount = 0;
		int tickerNewsCount = 0;
        long myStartTimeStamp = System.currentTimeMillis();
		try {
			for (MarketDataEntity<?> entity : replayService) {
				++count;
				
				if(Quote.class.isAssignableFrom(entity.getClass())) {
					++quoteCount;
				} else if(TradeIndication.class.isAssignableFrom(entity.getClass())) {
					++tradeIndicationCount;
				} else if(Candle.class.isAssignableFrom(entity.getClass())) {
					++candleCount;
				} else if(MarketDepthEvent.class.isAssignableFrom(entity.getClass())) {
					++marketDepthEventCount;
				} else if(TickerNews.class.isAssignableFrom(entity.getClass())) {
					++tickerNewsCount;
				}else{
					log.info("unhandled case:" + entity.getClass().getSimpleName());
				}
				replayService.fire(entity);
				//log.debug("fired " + entity +"("+entity.getClass().getSimpleName()+")"+ " for instrument: " + entity.getInstrumentSpecification());
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			log.error(ex);
		}
        long myEndTimeStamp = System.currentTimeMillis();        
        double myElapsedTime = myEndTimeStamp - myStartTimeStamp; 

        log.info("Backtester completed. Fired " + count + " events in " + (myElapsedTime/1000) + " seconds." );
        log.info("Breakdown " + quoteCount +" quotes; "+ candleCount +" candles; " + tradeIndicationCount + " tradeIndications; " + marketDepthEventCount +" marketDepthEvents; " + tickerNewsCount +" tickerNews.");
        // computing the events per second.
        if (myElapsedTime != 0) {
            double myThroughput = (count / myElapsedTime) * 1000.0;
            log.info("Throughput was " + myThroughput + "/s data entities.");
        }
	}
	/**
	 * returns the associated replayService(IMarketDataReplayService)
	 * @return
	 */
	public IMarketDataReplayService getReplayService() {
		return replayService;
	}
	/**
	 * sets the associated replayService(IMarketDataReplayService) with the given replayService(IMarketDataReplayService)
	 * @param replayService
	 */
	public void setReplayService(IMarketDataReplayService replayService) {
		this.replayService = replayService;
	}

}